home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / Apache 1.0 / src / mod_include.c < prev    next >
Text File  |  1995-12-04  |  26KB  |  874 lines

  1.  
  2. /* ====================================================================
  3.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer. 
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright
  13.  *    notice, this list of conditions and the following disclaimer in
  14.  *    the documentation and/or other materials provided with the
  15.  *    distribution.
  16.  *
  17.  * 3. All advertising materials mentioning features or use of this
  18.  *    software must display the following acknowledgment:
  19.  *    "This product includes software developed by the Apache Group
  20.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  21.  *
  22.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  23.  *    endorse or promote products derived from this software without
  24.  *    prior written permission.
  25.  *
  26.  * 5. Redistributions of any form whatsoever must retain the following
  27.  *    acknowledgment:
  28.  *    "This product includes software developed by the Apache Group
  29.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  32.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  33.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  35.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  42.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  43.  * ====================================================================
  44.  *
  45.  * This software consists of voluntary contributions made by many
  46.  * individuals on behalf of the Apache Group and was originally based
  47.  * on public domain software written at the National Center for
  48.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  49.  * For more information on the Apache Group and the Apache HTTP server
  50.  * project, please see <http://www.apache.org/>.
  51.  *
  52.  */
  53.  
  54.  
  55. /*
  56.  * http_include.c: Handles the server-parsed HTML documents
  57.  * 
  58.  * Original by Rob McCool; substantial fixups by David Robinson;
  59.  * incorporated into the Shambhala module framework by rst.
  60.  * 
  61.  */
  62.  
  63. #include "httpd.h"
  64. #include "http_config.h"
  65. #include "http_request.h"
  66. #include "http_core.h"
  67. #include "http_protocol.h"
  68. #include "http_log.h"
  69. #include "http_main.h"
  70. #include "util_script.h"
  71.  
  72. #define STARTING_SEQUENCE "<!--#"
  73. #define ENDING_SEQUENCE "-->"
  74. #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
  75. #define DEFAULT_TIME_FORMAT "%A, %d-%b-%y %T %Z"
  76. #define SIZEFMT_BYTES 0
  77. #define SIZEFMT_KMG 1
  78.  
  79. static void decodehtml(char *s);
  80. static char *get_tag(pool *p, FILE *in, char *tag, int tag_len, int dodecode);
  81. static int get_directive(FILE *in, char *d);
  82.  
  83. /* ------------------------ Environment function -------------------------- */
  84.  
  85. void add_include_vars(request_rec *r, char *timefmt)
  86. {
  87.     table *e = r->subprocess_env;
  88.     char *t;
  89.     time_t date = time(NULL);
  90.  
  91.     table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
  92.     table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
  93.     table_set(e, "LAST_MODIFIED",ht_time(r->pool,r->finfo.st_mtime,timefmt,0));
  94.     table_set(e, "DOCUMENT_URI", r->uri);
  95.     table_set(e, "DOCUMENT_PATH_INFO", r->path_info);
  96.     if((t = strrchr(r->filename, '/')))
  97.         table_set (e, "DOCUMENT_NAME", ++t);
  98.     else
  99.         table_set (e, "DOCUMENT_NAME", r->uri);
  100. }
  101.  
  102. #define GET_CHAR(f,c,r) \
  103.  { \
  104.    int i = getc(f); \
  105.    if(feof(f) || ferror(f) || (i == -1)) { \
  106.         fclose(f); \
  107.         return r; \
  108.    } \
  109.    c = (char)i; \
  110.  }
  111.  
  112. /* --------------------------- Parser functions --------------------------- */
  113.  
  114. /* Grrrr... rputc makes this slow as all-get-out.  Elsewhere, it doesn't
  115.  * matter much, but this is an inner loop...
  116.  */
  117.  
  118. int find_string(FILE *in,char *str, request_rec *r) {
  119.     int x,l=strlen(str),p;
  120.     char c;
  121.  
  122.     p=0;
  123.     while(1) {
  124.         GET_CHAR(in,c,1);
  125.         if(c == str[p]) {
  126.             if((++p) == l)
  127.                 return 0;
  128.         }
  129.         else {
  130.             if(r) {
  131.                 if(p) {
  132.                     for(x=0;x<p;x++) {
  133.                         rputc(str[x],r);
  134.                     }
  135.                 }
  136.                 rputc(c,r);
  137.             }
  138.             p=0;
  139.         }
  140.     }
  141. }
  142.  
  143. /*
  144.  * decodes a string containing html entities or numeric character references.
  145.  * 's' is overwritten with the decoded string.
  146.  * If 's' is syntatically incorrect, then the followed fixups will be made:
  147.  *   unknown entities will be left undecoded;
  148.  *   references to unused numeric characters will be deleted.
  149.  *   In particular, � will not be decoded, but will be deleted.
  150.  *
  151.  * drtr
  152.  */
  153.  
  154. /* maximum length of any ISO-LATIN-1 HTML entity name. */
  155. #define MAXENTLEN (6)
  156.  
  157. /* The following is a shrinking transformation, therefore safe. */
  158.  
  159. static void
  160. decodehtml(char *s)
  161. {
  162.     int val, i, j;
  163.     char *p=s;
  164.     char *ents;
  165.     static char *entlist[MAXENTLEN+1]={
  166.     NULL,  /* 0 */
  167.     NULL,  /* 1 */
  168.     "lt\074gt\076", /* 2 */
  169.     "amp\046ETH\320eth\360", /* 3 */
  170.     "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
  171. iuml\357ouml\366uuml\374yuml\377", /* 4 */
  172.     "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
  173. THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
  174. ucirc\373thorn\376", /* 5 */
  175.     "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
  176. Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
  177. Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
  178. egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
  179. otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */
  180.     };
  181.  
  182.     for (; *s != '\0'; s++, p++) {
  183.     if (*s != '&') {
  184.         *p = *s;
  185.         continue;
  186.     }
  187.     /* find end of entity */
  188.     for (i=1; s[i] != ';' && s[i] != '\0'; i++)
  189.         continue;
  190.  
  191.     if (s[i] == '\0') {    /* treat as normal data */
  192.         *p = *s;
  193.         continue;
  194.     }
  195.  
  196.     /* is it numeric ? */
  197.     if (s[1] == '#') {
  198.         for (j=2, val=0; j < i && isdigit(s[j]); j++)
  199.         val = val * 10 + s[j] - '0';
  200.         s += i;
  201.         if (j < i || val <= 8 || (val >= 11 && val <= 31) ||
  202.         (val >= 127 && val <= 160) || val >= 256)
  203.         p--;  /* no data to output */
  204.         else
  205.         *p = val;
  206.     } else{
  207.         j = i-1;
  208.         if (i-1 > MAXENTLEN || entlist[i-1] == NULL) { /* wrong length */
  209.         *p = '&';
  210.         continue;  /* skip it */
  211.         }
  212.         for (ents=entlist[i-1]; *ents != '\0'; ents += i)
  213.         if (strncmp(s+1, ents, i-1) == 0) break;
  214.  
  215.         if (*ents == '\0')
  216.         *p = '&';  /* unknown */
  217.         else {
  218.         *p = ((const unsigned char *)ents)[i-1];
  219.         s += i;
  220.         }
  221.     }
  222.     }
  223.  
  224.     *p = '\0';
  225. }
  226.  
  227. /*
  228.  * extract the next tag name and value.
  229.  * if there are no more tags, set the tag name to 'done'
  230.  * the tag value is html decoded if dodecode is non-zero
  231.  */
  232.  
  233. static char *
  234. get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode) {
  235.     char *t = tag, *tag_val, c, term;
  236.     int n;
  237.  
  238.     n = 0;
  239.  
  240.     do { /* skip whitespace */
  241.     GET_CHAR(in,c,NULL);
  242.     } while (isspace(c));
  243.  
  244.     /* tags can't start with - */
  245.     if(c == '-') {
  246.         GET_CHAR(in,c,NULL);
  247.         if(c == '-') {
  248.             do {
  249.         GET_CHAR(in,c,NULL);
  250.         } while (isspace(c));
  251.             if(c == '>') {
  252.                 strcpy(tag,"done");
  253.                 return tag;
  254.             }
  255.         }
  256.     return NULL; /* failed */
  257.     }
  258.  
  259.     /* find end of tag name */
  260.     while(1) {
  261.         if(++n == tagbuf_len) {
  262.             t[tagbuf_len - 1] = '\0';
  263.             return NULL;
  264.         }
  265.     if(c == '=' || isspace(c)) break;
  266.     *(t++) = tolower(c);
  267.         GET_CHAR(in,c,NULL);
  268.     }
  269.  
  270.     *t++ = '\0';
  271.     tag_val = t;
  272.  
  273.     while (isspace(c)) GET_CHAR(in, c, NULL); /* space before = */
  274.     if (c != '=') return NULL;
  275.  
  276.     do {
  277.     GET_CHAR(in,c,NULL);  /* space after = */
  278.     } while (isspace(c));
  279.  
  280.     /* we should allow a 'name' as a value */
  281.     
  282.     if (c != '"' && c != '\'') return NULL;
  283.     term = c;
  284.     while(1) {
  285.     GET_CHAR(in,c,NULL);
  286.     if(++n == tagbuf_len) {
  287.         t[tagbuf_len - 1] = '\0';
  288.         return NULL;
  289.     }
  290.     if (c == term) break;
  291.     *(t++) = c;
  292.     }
  293.     *t = '\0';
  294.     if (dodecode) decodehtml(tag_val);
  295.     return pstrdup (p, tag_val);
  296. }
  297.  
  298. static int
  299. get_directive(FILE *in, char *d) {
  300.     char c;
  301.  
  302.     /* skip initial whitespace */
  303.     while(1) {
  304.         GET_CHAR(in,c,1);
  305.         if(!isspace(c))
  306.             break;
  307.     }
  308.     /* now get directive */
  309.     while(1) {
  310.         *d++ = tolower(c);
  311.         GET_CHAR(in,c,1);
  312.         if(isspace(c))
  313.             break;
  314.     }
  315.     *d = '\0';
  316.     return 0;
  317. }
  318.  
  319. /* --------------------------- Action handlers ---------------------------- */
  320.  
  321. int include_cgi(char *s, request_rec *r)
  322. {
  323.     request_rec *rr = sub_req_lookup_uri (s, r);
  324.     
  325.     if (rr->status != 200) return -1;
  326.     
  327.     /* No hardwired path info or query allowed */
  328.     
  329.     if ((rr->path_info && rr->path_info[0]) || rr->args) return -1;
  330.     if (rr->finfo.st_mode == 0) return -1;
  331.  
  332.     /* Script gets parameters of the *document*, for back compatibility */
  333.     
  334.     rr->path_info = r->path_info; /* painful to get right; see mod_cgi.c */
  335.     rr->args = r->args;
  336.     
  337.     /* Force sub_req to be treated as a CGI request, even if ordinary
  338.      * typing rules would have called it something else.
  339.      */
  340.  
  341.     rr->content_type = CGI_MAGIC_TYPE;
  342.  
  343.     /* Run it. */
  344.     
  345.     if (run_sub_req (rr) == REDIRECT) {
  346.         char *location = table_get (rr->headers_out, "Location");
  347.     rprintf(r,"<A HREF=\"%s\">%s</A>",location,location);
  348.     }
  349.     
  350.     destroy_sub_req (rr);
  351.     
  352.     return 0;
  353. }
  354.  
  355. int handle_include(FILE *in, request_rec *r, char *error, int noexec) {
  356.     char tag[MAX_STRING_LEN],errstr[MAX_STRING_LEN];
  357.     char *tag_val;
  358.  
  359.     while(1) {
  360.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
  361.             return 1;
  362.         if(!strcmp(tag,"file") || !strcmp (tag, "virtual")) {
  363.         request_rec *rr=NULL;
  364.         char *error_fmt = NULL;
  365.  
  366.         if (tag[0] == 'f')
  367.         { /* be safe; only files in this directory or below allowed */
  368.         char tmp[MAX_STRING_LEN+2];
  369.         sprintf(tmp, "/%s/", tag_val);
  370.         if (tag_val[0] == '/' || strstr(tmp, "/../") != NULL)
  371.             error_fmt = "unable to include file %s in parsed file %s";
  372.         else
  373.             rr = sub_req_lookup_file (tag_val, r);
  374.         } else
  375.         rr = sub_req_lookup_uri (tag_val, r);
  376.         
  377.         if (!error_fmt && rr->status != 200)
  378.             error_fmt = "unable to include %s in parsed file %s";
  379.  
  380.         if (!error_fmt && noexec && rr->content_type
  381.         && (strncmp (rr->content_type, "text/", 5)))
  382.             error_fmt =
  383.           "unable to include potential exec %s in parsed file %s";
  384.         
  385.         if (!error_fmt && run_sub_req (rr))
  386.             error_fmt = "unable to include %s in parsed file %s";
  387.             
  388.             if (error_fmt) {
  389.                 sprintf(errstr, error_fmt, tag_val, r->filename);
  390.                 log_error (errstr, r->server);
  391.                 rprintf(r,"%s",error);
  392.             }            
  393.  
  394.         if (rr != NULL) destroy_sub_req (rr);
  395.         } 
  396.         else if(!strcmp(tag,"done"))
  397.             return 0;
  398.         else {
  399.             sprintf(errstr,"unknown parameter %s to tag include in %s",tag,
  400.             r->filename);
  401.             log_error (errstr, r->server);
  402.             rprintf(r,"%s",error);
  403.         }
  404.     }
  405. }
  406.  
  407. typedef struct {
  408.     request_rec *r;
  409.     char *s;
  410. } include_cmd_arg;
  411.  
  412. void include_cmd_child (void *arg)
  413. {
  414.     request_rec *r =  ((include_cmd_arg *)arg)->r;
  415.     char *s = ((include_cmd_arg *)arg)->s;
  416.     table *env = r->subprocess_env;
  417. #ifdef DEBUG_INCLUDE_CMD    
  418.     FILE *dbg = fopen ("/dev/tty", "w");
  419. #endif    
  420.     char err_string [MAX_STRING_LEN];
  421.  
  422. #ifdef DEBUG_INCLUDE_CMD    
  423.     fprintf (dbg, "Attempting to include command '%s'\n", s);
  424. #endif    
  425.  
  426.     if (r->path_info && r->path_info[0] != '\0')
  427.     {
  428.     request_rec *pa_req;
  429.  
  430.     table_set (env, "PATH_INFO", escape_shell_cmd (r->pool, r->path_info));
  431.     
  432.     pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r);
  433.     if (pa_req->filename)
  434.         table_set(env, "PATH_TRANSLATED",
  435.               pstrcat(r->pool, pa_req->filename, pa_req->path_info,
  436.                   NULL));
  437.     }
  438.  
  439.     if (r->args) {
  440.         table_set (env, "QUERY_STRING", r->args);
  441.     unescape_url (r->args);
  442.     table_set (env, "QUERY_STRING_UNESCAPED",
  443.            escape_shell_cmd (r->pool, r->args));
  444.     }
  445.     
  446.     error_log2stderr (r->server);
  447.     
  448. #ifdef DEBUG_INCLUDE_CMD    
  449.     fprintf (dbg, "Attempting to exec '%s'\n", s);
  450. #endif    
  451.     cleanup_for_exec();
  452.     execle(SHELL_PATH, SHELL_PATH, "-c", s, NULL,
  453.        create_environment (r->pool, env));
  454.     
  455.     /* Oh, drat.  We're still here.  The log file descriptors are closed,
  456.      * so we have to whimper a complaint onto stderr...
  457.      */
  458.     
  459. #ifdef DEBUG_INCLUDE_CMD    
  460.     fprintf (dbg, "Exec failed\n");
  461. #endif    
  462.     sprintf(err_string, "httpd: exec of %s failed, errno is %d\n",
  463.         SHELL_PATH,errno);
  464.     write (2, err_string, strlen(err_string));
  465.     exit(0);
  466. }
  467.  
  468. int include_cmd(char *s, request_rec *r) {
  469.     include_cmd_arg arg;
  470.     FILE *f;
  471.  
  472.     arg.r = r; arg.s = s;
  473.  
  474.     if (!spawn_child (r->connection->pool, include_cmd_child, &arg,
  475.               kill_after_timeout, NULL, &f))
  476.         return -1;
  477.     
  478.     send_fd(f,r);
  479.     pfclose(r->pool, f);    /* will wait for zombie when
  480.                  * r->pool is cleared
  481.                  */
  482.     return 0;
  483. }
  484.  
  485.  
  486. int handle_exec(FILE *in, request_rec *r, char *error)
  487. {
  488.     char tag[MAX_STRING_LEN],errstr[MAX_STRING_LEN];
  489.     char *tag_val;
  490.     char *file = r->filename;
  491.  
  492.     while(1) {
  493.         if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1)))
  494.             return 1;
  495.         if(!strcmp(tag,"cmd")) {
  496.             if(include_cmd(tag_val, r) == -1) {
  497.                 sprintf(errstr,"failed command exec %s in %s",tag_val,file);
  498.                 log_error (errstr, r->server);
  499.                 rprintf(r,"%s",error);
  500.             }
  501.             /* just in case some stooge changed directories */
  502.             chdir_file(r->filename);
  503.         } 
  504.         else if(!strcmp(tag,"cgi")) {
  505.             if(include_cgi(tag_val, r) == -1) {
  506.                 sprintf(errstr,"invalid CGI ref %s in %s",tag_val,file);
  507.                 log_error (errstr, r->server);
  508.                 rprintf(r,"%s",error);
  509.             }
  510.             /* grumble groan */
  511.             chdir_file(r->filename);
  512.         }
  513.         else if(!strcmp(tag,"done"))
  514.             return 0;
  515.         else {
  516.             char errstr[MAX_STRING_LEN];
  517.             sprintf(errstr,"unknown parameter %s to tag exec in %s",tag,file);
  518.             log_error (errstr, r->server);
  519.             rprintf(r, "%s",error);
  520.         }
  521.     }
  522.  
  523. }
  524.  
  525. int handle_echo (FILE *in, request_rec *r, char *error) {
  526.     char tag[MAX_STRING_LEN];
  527.     char *tag_val;
  528.  
  529.     while(1) {
  530.         if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1)))
  531.             return 1;
  532.         if(!strcmp(tag,"var")) {
  533.         char *val = table_get (r->subprocess_env, tag_val);
  534.  
  535.         if (val) rprintf (r, "%s", val);
  536.         else rprintf (r, "(none)");
  537.         } else if(!strcmp(tag,"done"))
  538.             return 0;
  539.         else {
  540.             char errstr[MAX_STRING_LEN];
  541.             sprintf(errstr,"unknown parameter %s to tag echo in %s",
  542.             tag, r->filename);
  543.             log_error(errstr, r->server);
  544.             rprintf (r, "%s", error);
  545.         }
  546.     }
  547. }
  548.  
  549. int handle_config(FILE *in, request_rec *r, char *error, char *tf,
  550.                   int *sizefmt) {
  551.     char tag[MAX_STRING_LEN];
  552.     char *tag_val;
  553.     table *env = r->subprocess_env;
  554.  
  555.     while(1) {
  556.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0)))
  557.             return 1;
  558.         if(!strcmp(tag,"errmsg"))
  559.             strcpy(error,tag_val);
  560.         else if(!strcmp(tag,"timefmt")) {
  561.         time_t date = time(NULL);
  562.             strcpy(tf,tag_val);
  563.             table_set (env, "DATE_LOCAL", ht_time(r->pool,date,tf,0));
  564.             table_set (env, "DATE_GMT", ht_time(r->pool,date,tf,1));
  565.             table_set (env, "LAST_MODIFIED", ht_time(r->pool,r->finfo.st_mtime,tf,0));
  566.         }
  567.         else if(!strcmp(tag,"sizefmt")) {
  568.         decodehtml(tag_val);
  569.             if(!strcmp(tag_val,"bytes"))
  570.                 *sizefmt = SIZEFMT_BYTES;
  571.             else if(!strcmp(tag_val,"abbrev"))
  572.                 *sizefmt = SIZEFMT_KMG;
  573.         } 
  574.         else if(!strcmp(tag,"done"))
  575.             return 0;
  576.         else {
  577.             char errstr[MAX_STRING_LEN];
  578.             sprintf(errstr,"unknown parameter %s to tag config in %s",
  579.                     tag, r->filename);
  580.             log_error(errstr, r->server);
  581.             rprintf (r,"%s",error);
  582.         }
  583.     }
  584. }
  585.  
  586.  
  587.  
  588. int find_file(request_rec *r, char *directive, char *tag, 
  589.               char *tag_val, struct stat *finfo, char *error)
  590. {
  591.     char errstr[MAX_STRING_LEN], dir[MAX_STRING_LEN];
  592.     char *to_send;
  593.  
  594.     if(!strcmp(tag,"file")) {
  595.         getparents(tag_val); /* get rid of any nasties */
  596.         getwd(dir);
  597.         to_send = make_full_path (r->pool, dir, tag_val);
  598.         if(stat(to_send,finfo) == -1) {
  599.             sprintf(errstr,
  600.                     "unable to get information about %s in parsed file %s",
  601.                     to_send, r->filename);
  602.             log_error (errstr, r->server);
  603.             rprintf (r,"%s",error);
  604.             return -1;
  605.         }
  606.         return 0;
  607.     }
  608.     else if(!strcmp(tag,"virtual")) {
  609.     request_rec *rr = sub_req_lookup_uri (tag_val, r);
  610.     
  611.     if (rr->status == 200 && rr->finfo.st_mode != 0) {
  612.         memcpy ((char*)finfo, (const char *)&rr->finfo, sizeof (struct stat));
  613.         destroy_sub_req (rr);
  614.         return 0;
  615.         } else {
  616.             sprintf(errstr,
  617.                     "unable to get information about %s in parsed file %s",
  618.                     tag_val, r->filename);
  619.             log_error(errstr, r->server);
  620.             rprintf(r,"%s",error);
  621.         destroy_sub_req (rr);
  622.             return -1;
  623.         }
  624.     }
  625.     else {
  626.         sprintf(errstr,"unknown parameter %s to tag %s in %s",
  627.                 tag, directive, r->filename);
  628.         log_error(errstr, r->server);
  629.         rprintf(r,"%s",error);
  630.         return -1;
  631.     }
  632. }
  633.  
  634.  
  635. int handle_fsize(FILE *in, request_rec *r, char *error, int sizefmt) 
  636. {
  637.     char tag[MAX_STRING_LEN];
  638.     char *tag_val;
  639.     struct stat finfo;
  640.  
  641.     while(1) {
  642.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
  643.             return 1;
  644.         else if(!strcmp(tag,"done"))
  645.             return 0;
  646.         else if(!find_file(r,"fsize",tag,tag_val,&finfo,error)) {
  647.             if(sizefmt == SIZEFMT_KMG) {
  648.                 send_size(finfo.st_size, r);
  649.             }
  650.             else {
  651.                 int l,x;
  652. #if defined(BSD) && BSD > 199305
  653.                 sprintf(tag,"%qd",finfo.st_size);
  654. #else
  655.                 sprintf(tag,"%ld",finfo.st_size);
  656. #endif
  657.                 l = strlen(tag); /* grrr */
  658.                 for(x=0;x<l;x++) {
  659.                     if(x && (!((l-x) % 3))) {
  660.                         rputc(',', r);
  661.                     }
  662.                     rputc (tag[x],r);
  663.                 }
  664.             }
  665.         }
  666.     }
  667. }
  668.  
  669. int handle_flastmod(FILE *in, request_rec *r, char *error, char *tf) 
  670. {
  671.     char tag[MAX_STRING_LEN];
  672.     char *tag_val;
  673.     struct stat finfo;
  674.  
  675.     while(1) {
  676.         if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
  677.             return 1;
  678.         else if(!strcmp(tag,"done"))
  679.             return 0;
  680.         else if(!find_file(r,"flastmod",tag,tag_val,&finfo,error))
  681.             rprintf (r, "%s", ht_time(r->pool, finfo.st_mtime, tf, 0));
  682.     }
  683. }    
  684.  
  685.  
  686.  
  687. /* -------------------------- The main function --------------------------- */
  688.  
  689. /* This is a stub which parses a file descriptor. */
  690.  
  691. void send_parsed_content(FILE *f, request_rec *r)
  692. {
  693.     char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
  694.     char timefmt[MAX_STRING_LEN], errstr[MAX_STRING_LEN];
  695.     int noexec = allow_options (r) & OPT_INCNOEXEC;
  696.     int ret, sizefmt;
  697.  
  698.     strcpy(error,DEFAULT_ERROR_MSG);
  699.     strcpy(timefmt,DEFAULT_TIME_FORMAT);
  700.     sizefmt = SIZEFMT_KMG;
  701.  
  702.     chdir_file (r->filename);
  703.  
  704.     while(1) {
  705.         if(!find_string(f,STARTING_SEQUENCE,r)) {
  706.             if(get_directive(f,directive))
  707.                 return;
  708.             if(!strcmp(directive,"exec")) {
  709.                 if(noexec) {
  710.                     sprintf(errstr,"httpd: exec used but not allowed in %s",
  711.                             r->filename);
  712.                     log_error (errstr, r->server);
  713.                     rprintf(r,"%s",error);
  714.                     ret = find_string(f,ENDING_SEQUENCE,NULL);
  715.                 } else 
  716.                     ret=handle_exec(f, r, error);
  717.             } 
  718.             else if(!strcmp(directive,"config"))
  719.                 ret=handle_config(f, r, error, timefmt, &sizefmt);
  720.             else if(!strcmp(directive,"include"))
  721.                 ret=handle_include(f, r, error, noexec);
  722.             else if(!strcmp(directive,"echo"))
  723.                 ret=handle_echo(f, r, error);
  724.             else if(!strcmp(directive,"fsize"))
  725.                 ret=handle_fsize(f, r, error, sizefmt);
  726.             else if(!strcmp(directive,"flastmod"))
  727.                 ret=handle_flastmod(f, r, error, timefmt);
  728.             else {
  729.                 sprintf(errstr,"httpd: unknown directive %s in parsed doc %s",
  730.                         directive,r->filename);
  731.                 log_error (errstr, r->server);
  732.                 rprintf (r,"%s",error);
  733.                 ret=find_string(f,ENDING_SEQUENCE,NULL);
  734.             }
  735.             if(ret) {
  736.                 sprintf(errstr,"httpd: premature EOF in parsed file %s",
  737.             r->filename);
  738.                 log_error(errstr, r->server);
  739.                 return;
  740.             }
  741.         } else 
  742.             return;
  743.     }
  744. }
  745.  
  746. /*****************************************************************
  747.  *
  748.  * XBITHACK.  Sigh...  NB it's configurable per-directory; the compile-time
  749.  * option only changes the default.
  750.  */
  751.  
  752. module includes_module;
  753. enum xbithack { xbithack_off, xbithack_on, xbithack_full };
  754.  
  755. #ifdef XBITHACK    
  756. #define DEFAULT_XBITHACK xbithack_full
  757. #else
  758. #define DEFAULT_XBITHACK xbithack_off
  759. #endif
  760.  
  761. void *create_includes_dir_config (pool *p, char *dummy)
  762. {
  763.     enum xbithack *result = (enum xbithack*)palloc(p, sizeof (enum xbithack));
  764.     *result = DEFAULT_XBITHACK;
  765.     return result;
  766. }
  767.  
  768. char *set_xbithack (cmd_parms *cmd, void *xbp, char *arg)
  769. {
  770.    enum xbithack *state = (enum xbithack *)xbp;
  771.  
  772.    if (!strcasecmp (arg, "off")) *state = xbithack_off;
  773.    else if (!strcasecmp (arg, "on")) *state = xbithack_on;
  774.    else if (!strcasecmp (arg, "full")) *state = xbithack_full;
  775.    else return "XBitHack must be set to Off, On, or Full";
  776.  
  777.    return NULL;
  778. }
  779.  
  780. int send_parsed_file(request_rec *r)
  781. {
  782.     FILE *f;
  783.     enum xbithack *state =
  784.     (enum xbithack *)get_module_config(r->per_dir_config,&includes_module);
  785.     int errstatus;
  786.  
  787.     if (!(allow_options (r) & OPT_INCLUDES)) return DECLINED;
  788.     if (r->method_number != M_GET) return DECLINED;
  789.     if (r->finfo.st_mode == 0) return NOT_FOUND;
  790.     
  791.     if (*state == xbithack_full
  792.     && (r->finfo.st_mode & S_IXGRP)
  793.     && (errstatus = set_last_modified (r, r->finfo.st_mtime)))
  794.         return errstatus;
  795.     
  796.     if(!(f=pfopen(r->pool, r->filename, "r"))) {
  797.         log_reason("file permissions deny server access", r->filename, r);
  798.     return FORBIDDEN;
  799.     }
  800.     
  801.     r->content_type = "text/html";
  802.     
  803.     hard_timeout ("send", r);
  804.     send_http_header(r);
  805.  
  806.     if (r->header_only) {
  807.         kill_timeout (r);
  808.     pfclose (r->pool, f);
  809.     return OK;
  810.     }
  811.    
  812.     if (r->main) {
  813.     /* Kludge --- for nested includes, we want to keep the
  814.      * subprocess environment of the base document (for compatibility);
  815.      * that means torquing our own last_modified date as well so that
  816.      * the LAST_MODIFIED variable gets reset to the proper value if
  817.      * the nested document resets <!--#config timefmt-->
  818.      */
  819.     r->subprocess_env = r->main->subprocess_env;
  820.     r->finfo.st_mtime= r->main->finfo.st_mtime;
  821.     } else { 
  822.     add_common_vars (r);
  823.     add_include_vars (r, DEFAULT_TIME_FORMAT);
  824.     }
  825.     
  826.     send_parsed_content (f, r);
  827.     
  828.     kill_timeout (r);
  829.     return OK;
  830. }
  831.  
  832. int xbithack_handler (request_rec *r)
  833. {
  834.     enum xbithack *state;
  835.     
  836.     if (!(r->finfo.st_mode & S_IXUSR)) return DECLINED;
  837.  
  838.     state = (enum xbithack *)get_module_config(r->per_dir_config,
  839.                            &includes_module);
  840.     
  841.     if (*state == xbithack_off) return DECLINED;
  842.     return send_parsed_file (r);
  843. }
  844.  
  845. command_rec includes_cmds[] = {
  846. { "XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full" },
  847. { NULL }    
  848. };
  849.  
  850. handler_rec includes_handlers[] = {
  851. { INCLUDES_MAGIC_TYPE, send_parsed_file },
  852. { INCLUDES_MAGIC_TYPE3, send_parsed_file },
  853. { "*/*", xbithack_handler },
  854. { NULL }
  855. };
  856.  
  857. module includes_module = {
  858.    STANDARD_MODULE_STUFF,
  859.    NULL,            /* initializer */
  860.    create_includes_dir_config,    /* dir config creater */
  861.    NULL,            /* dir merger --- default is to override */
  862.    NULL,            /* server config */
  863.    NULL,            /* merge server config */
  864.    includes_cmds,        /* command table */
  865.    includes_handlers,        /* handlers */
  866.    NULL,            /* filename translation */
  867.    NULL,            /* check_user_id */
  868.    NULL,            /* check auth */
  869.    NULL,            /* check access */
  870.    NULL,            /* type_checker */
  871.    NULL,            /* fixups */
  872.    NULL                /* logger */
  873. };
  874.